﻿using System;
using System.Collections.Generic;
using ParserGenerator.SyntacticAnalysis;

namespace ParserGenerator.GrammarAnalysis
{
    [Serializable]
    internal sealed class RecursiveLexicalAliasChecker : IRecursiveLexicalAliasChecker
    {
        private readonly LexicalAliasTable _table;
        private readonly LexicalAlias _entry;
        private readonly Dictionary<LexicalAlias, bool?> _dict;

        public RecursiveLexicalAliasChecker(LexicalAliasTable table, LexicalAlias entry)
        {
            if (table == null)
                throw new ArgumentNullException(nameof(table));
            if (entry == null)
                throw new ArgumentNullException(nameof(entry));
            _table = table;
            _entry = entry;
            _dict = new Dictionary<LexicalAlias, bool?>();
            _dict.Add(entry, null);
        }

        public bool CheckLexicalExpression(LexicalExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            return n.AcceptRecursiveLexicalAliasChecker(this);
        }

        public bool CheckEmptyLexicalExpression(EmptyLexicalExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            return false;
        }

        public bool CheckStringLexicalExpression(StringLexicalExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            return false;
        }

        public bool CheckQuoteStringLexicalExpression(QuoteStringLexicalExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            return false;
        }

        public bool CheckCharClassLexicalExpression(CharClassLexicalExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            return false;
        }

        public bool CheckNameReferenceLexicalExpression(NameReferenceLexicalExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            LexicalAlias alias = _table.Lookup(n.Name);
            bool? status;
            if (_dict.TryGetValue(alias, out status))
            {
                if (status.HasValue)
                    return status.Value;
                _dict[alias] = true;
                return true;
            }
            _dict[alias] = null;
            bool value = CheckLexicalExpression(alias.Pattern);
            _dict[alias] = value;
            return value;
        }

        public bool CheckKleeneLexicalExpression(KleeneLexicalExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            return CheckLexicalExpression(n.Operand);
        }

        public bool CheckOptionalLexicalExpression(OptionalLexicalExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            return CheckLexicalExpression(n.Operand);
        }

        public bool CheckPositiveLexicalExpression(PositiveLexicalExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            return CheckLexicalExpression(n.Operand);
        }

        public bool CheckRepeatLexicalExpression(RepeatLexicalExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            return CheckLexicalExpression(n.Operand);
        }

        public bool CheckConcatLexicalExpression(ConcatLexicalExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (CheckLexicalExpression(n.Left))
                return true;
            if (CheckLexicalExpression(n.Right))
                return true;
            return false;
        }

        public bool CheckUnionLexicalExpression(UnionLexicalExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (CheckLexicalExpression(n.Left))
                return true;
            if (CheckLexicalExpression(n.Right))
                return true;
            return false;
        }
    }
}
